/*
 * BtreeNode.cpp
 *
 *  Created on: Oct 26, 2011
 *      Author: hxu
 */

#include "BtreeNode.h"
#include<cstdlib>
#include<string>
#include<limits>
#include<cmath>
#include<sstream>
#include<iostream>

using namespace std;

namespace Cxuh {

const double ELMORE_DISTR = 0.38; // distributed Elmore coefficients
const double ELMORE_LUMP = 0.69; // lumped Elmore coefficients

void BtreeNode::initBtreeNode() {
	// initialization used for all constructors
	parent = NULL;
	lchild = NULL;
	rchild = NULL;
	pre_wire = NULL;
	name = "";
	dmax = numeric_limits<double>::min();
	dmin = numeric_limits<double>::max();
	csink = 0;
}

BtreeNode::BtreeNode() :
		x(0), y(0), z(0) {
	// empty node
	initBtreeNode();
	id = -1;
	name = "";
}

BtreeNode::BtreeNode(int id1, char nodetype) :
		type(nodetype), x(0), y(0), z(0) {
	// Auto-generated constructor stub
	initBtreeNode();
	this->id = id1;
	stringstream sstr;
	sstr << nodetype << id;
	sstr >> name;
}

BtreeNode::BtreeNode(int id1, char nodetype, int x1, int y1, int z1) :
		type(nodetype), x(x1), y(y1), z(z1) {
	// Auto-generated constructor stub
	initBtreeNode();
	this->id = id1;
	stringstream sstr;
	sstr << nodetype << id;
	sstr >> name;
}

BtreeNode::~BtreeNode() {
	// Auto-generated destructor stub
}

string BtreeNode::getName() const {
	// get the name of this node
	//stringstream sstr;
	//sstr << type << id;
	//sstr >> name; // update the name
	return name;
}

int operator -(const BtreeNode& n1, const BtreeNode& n2) {
	// subtraction of coordinates, Manhattan distance
	int d, d1, d2;
	d1 = n1.getX() - n2.getX();
	d2 = n1.getY() - n2.getY();

	d = abs(d1) + abs(d2);

	return d;
}

std::ostream& operator<<(std::ostream& out, BtreeNode& obj) {

	string ntype;
	BtreeNode* par = obj.getParent();

	switch (obj.getType()) {
	case 'm':
		ntype = "Merge Point : ";
		break;
	case 's':
		ntype = "Sink : ";
		break;
	case 't':
		ntype = "TSV : ";
		break;
	case 'b':
		ntype = "BUF : ";
		break;
	default:
		ntype = obj.type;
		break;
	}

	// print previous net
	if (par) {
		string pname = par->name;
		string name = obj.name;
		Wire *wire1 = obj.pre_wire;

		if (wire1) {
			string net_name = wire1->getName();
			out << "Net : " << net_name << endl;
			out << "\tCoordinate : " << par->x << " " << par->y << " " << obj.x
					<< " " << obj.y << endl;
			out << "\tLength : " << wire1->getL() << endl;
		} else {
			string errstr = " Error: there is no wire between " + pname
					+ " and " + name;
			throw(errstr.c_str());
		}
	}

	// print node
	out << ntype << obj.getID() << endl;
	out << "\tCoordinate : " << obj.getX() << " " << obj.getY() << " "
			<< obj.getZ();
	if (obj.getType() == 't')
		out << " " << obj.getZ2(); // 2nd tier connected to TSV
	out << std::endl;
	out << "\tCapacitive Load : " << obj.getCin() << std::endl;

	return out;
}

void BtreeNode::calc_elmore_delay_tosink() {
	// calculate the min and max path delay rooted here
	double dlmin = numeric_limits<double>::max(), dlmax = -1;
	double drmin = numeric_limits<double>::max(), drmax = -1;
	double cl = 0, cr = 0;
	double d = 0, d0 = 0;

	if (type == 's') {
		//leaf
		dmin = 0;
		dmax = 0;
	} else {
		if (lchild) {
			Wire* lw = lchild->pre_wire;
			if (!lw)
				throw(" Error: there is a BtreeNode without pre_wire!");

			lchild->calc_elmore_delay_tosink(); // update dmin and dmax of lchild

			cl = lchild->get_in_cload();
			d = lw->getD() + ELMORE_LUMP * lw->getR() * cl;
			dlmin = lchild->dmin + d;
			dlmax = lchild->dmax + d;
		}

		if (rchild) {
			Wire* rw = rchild->pre_wire;
			if (!rw)
				throw(" Error: there is a BtreeNode without pre_wire!");

			rchild->calc_elmore_delay_tosink();

			cl = rchild->get_in_cload();
			d = rw->getD() + ELMORE_LUMP * rw->getR() * cl;
			drmin = rchild->dmin + d;
			drmax = rchild->dmax + d;
		}

		d0 = getD() + ELMORE_LUMP * getRout() * (cl + cr); // intrinsic delay

		dmax = max(drmax, dlmax) + d0;
		dmin = min(drmin, dlmin) + d0;
	}
}

double BtreeNode::get_in_cload() const {
	// get the c load seen at the input of this node
	double inload = 0;
	double clw = 0, crw = 0; // cap of left and right succeeding wires
	double c1, c2; // cap of lchild and rchild

	if (this->type == 's' || this->type == 'b')
		inload = this->getCin();
	else {
		// TSV or merge point
		if (this->lchild) {
			clw = this->lchild->pre_wire->getC();
			c1 = lchild->get_in_cload();
		} else {
			clw = 0;
			c1 = 0;
		}
		if (this->rchild) {
			crw = rchild->pre_wire->getC();
			c2 = rchild->get_in_cload();
		} else {
			crw = 0;
			c2 = 0;
		}

		inload = this->getCin() + clw + c1 + c2 + crw;
	}

	return inload;
}

void BtreeNode::setLchild(BtreeNode * lchd) {
	lchild = lchd;
	if (lchd)
		lchd->parent = this;
}
void BtreeNode::setRchild(BtreeNode * rchd) {
	rchild = rchd;
	if (rchd)
		rchd->parent = this;
}

void BtreeNode::setChild(BtreeNode * chd) {
	if (!lchild)
		setLchild(chd);
	else if (!rchild)
		setRchild(chd);
	else
		throw(" Error: insert a child to a full node!");

	chd->parent = this;
}

void BtreeNode::setID(int i) {
	id = i;
	stringstream ss;
	ss << type << id;
	ss >> name;
}

int BtreeNode::getZ2() {
	string errstr = " Error: call getZ2 in BtreeNode " + name + "!";
	throw(errstr.c_str());
}

void BtreeNode::setXYZ(int, int, int, int) {
	string errstr = " Error: call setXYZ in BtreeNode " + name + "!";
	throw(errstr.c_str());
}

}
